//////////////////////////////////////////////////////////////////////////////
//       Copyright (c) 2003 - RealTime Gaming.  All rights reserved.        //
//////////////////////////////////////////////////////////////////////////////

//////////////////////////////////////////////////////////////////////////////
// Includes
//////////////////////////////////////////////////////////////////////////////
#include <string.h>

#include "lib.h"
#include "libglob.h"
#include "rmanager.h"
#include "rresource.h"
#include "strtable.h"
using namespace Library;

//////////////////////////////////////////////////////////////////////////////
/// Default Constructor
//////////////////////////////////////////////////////////////////////////////
StrTable::StrTable( KeywordSubstitutor* pSubstitutor, LocaleFormatter* pFormatter ) 
    : m_pTable(NULL), 
      m_nRefCount(0), 
      m_pSubstitutor(pSubstitutor), 
      m_pFormatter(pFormatter)
{
	m_pTable = new HashTable<String*>();

    ASSERT( m_pTable );
}

//////////////////////////////////////////////////////////////////////////////
/// Destructor
//////////////////////////////////////////////////////////////////////////////
StrTable::~StrTable()
{
	// Destroy all strings in the hash table

	// Scope this so the iterator goes out of scope before we destroy the
	//  table.  This is the only place we should really have to do this.
	{
		HashTableIterator<String*> it(m_pTable);

		for( it.begin(); it.key(); it++ )
		{
            // Mark string as dirty in the keyword substitutor
            if ( m_pSubstitutor )
            {
                String sCacheName = m_sTableName;
                sCacheName += *it.key();
                m_pSubstitutor->DirtyCacheString( sCacheName );
            }

            // Delete the memory
			delete it.value();
		}
	}

	// Destroy the hash table
	delete m_pTable;
}

//////////////////////////////////////////////////////////////////////////////
/// Clears the contents of the entire string table.
//////////////////////////////////////////////////////////////////////////////
void 
StrTable::Clear()
{
    // First, mark all strings dirty that may be in the keyword manager
    if ( m_pSubstitutor )
    {
	    HashTableIterator<String*> it(m_pTable);
	    for( it.begin(); it.key(); it++ )
	    {
            String sCacheName = m_sTableName;
            sCacheName += *it.key();
            m_pSubstitutor->DirtyCacheString( sCacheName );
	    }
    }

	// Clear the hash table
	if ( m_pTable )
	{
		m_pTable->clear();
	}
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if successful.
///
/// @param sTableName	This is the string table name as stored in the
///                     resource file.
///
/// This method will load a string table from the application resources.
//////////////////////////////////////////////////////////////////////////////
bool8 
StrTable::LoadFromResource( const String& sTableName )
{
	bool8 bRetVal = false;

    // Check internal pointer
    if( !m_pFormatter )
    {
        ASSERT(false);
        return( bRetVal );
    }

	// First, clear us out
	Clear();

	// Now, load the string table from the resource
	if ( !sTableName.isEmpty() )
	{
        // Load RSC file
        String sRscFileName = "rsc\\";
        sRscFileName += sTableName;
        sRscFileName += ".";
        sRscFileName += m_pFormatter->GetCurrentLanguageCode();
        sRscFileName += ".st.rsc";
        uint32 nRSC = gResourceManager->open(sRscFileName);

		// Create the filename to load from the resource
		String sTableFileName(m_pFormatter->GetCurrentLanguageCode());
        sTableFileName += "/";
        sTableFileName += sTableName;
		sTableFileName += ".str";
        sTableFileName.toLower();

		// Get a pointer to the data from the resource manager
		uint8* pResData = (uint8*)gResourceManager->get(sTableFileName);

        // kharmon.  03-06-06.  #10022.  If we don't find a foriegn language string table
        // check to see if there's an english language version, and if so, load it instead.
        if(pResData==NULL)
        {
            // Close resource file, just in case something was actually opened.
		    gResourceManager->close(nRSC);

            sRscFileName = "rsc\\";
            sRscFileName += sTableName;
            sRscFileName += ".en.st.rsc";
            nRSC = gResourceManager->open(sRscFileName);

		    // Create the filename to load from the resource		    
            sTableFileName = "EN/";
            sTableFileName += sTableName;
		    sTableFileName += ".str";
            sTableFileName.toLower();

		    // Get a pointer to the data from the resource manager
		    pResData = (uint8*)gResourceManager->get(sTableFileName);
        }

		if (pResData)
		{
			bRetVal = LoadFromBinary(pResData);
			
			// Store the table name
			if ( bRetVal )
			{
				SetName( sTableName );
			}

			// Release the resource
			Resource::release(pResData);
		}
        else
        {
            DEBUGMSG(("ERROR: Unable to load string table file %S in %S!", (const uint16*) sTableFileName, (const uint16*) sRscFileName));
        }

        // Close resource file
		gResourceManager->close(nRSC);
	}

	return bRetVal;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				"const" reference to a string in the table.
///
/// @param sShortName	Key (shortname) of the string to locate in the table.
///
/// This method will search the string table for a string and return a 
///  reference to that string object if found. 
//////////////////////////////////////////////////////////////////////////////
const String&
StrTable::GetString( const String& sShortName ) const
{
	String* pTemp;
	if ( m_pTable->get( sShortName, &pTemp ) )
	{
		return ( *pTemp );
	}

	// Return a reference to our empty string if the string is not found in
	//   the table.
	// NOTE: We do this to avoid unnecessary copying of the string.
	return m_sEmpty;
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if the new string was successfully added to the
///						 table.
///
/// @param sShortName	Key (shortname) of the string to locate in the table.
///
/// @param string		The actual string to add.
///
/// This method will add a new string to the string table. 
//////////////////////////////////////////////////////////////////////////////
bool8
StrTable::AddString( const String& sShortName, const String& string )
{
	return( m_pTable->add( sShortName, new String( string ) ) );
}

//////////////////////////////////////////////////////////////////////////////
/// @return				True if the new string was found and removed.
///
/// @param sShortName	Key (shortname) of the string to locate in the table.
///
/// This method will remove a string from the string table. 
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
bool8
StrTable::RemoveString( const String& sShortName )
{
	return ( m_pTable->remove( sShortName ) );
}

//////////////////////////////////////////////////////////////////////////////
/// Add one to the reference count on this table.
//////////////////////////////////////////////////////////////////////////////
void
StrTable::AddRef()
{
	++m_nRefCount;
}

//////////////////////////////////////////////////////////////////////////////
/// Release one from the reference count on this table.
//////////////////////////////////////////////////////////////////////////////
void
StrTable::Release()
{
	--m_nRefCount;
}

//////////////////////////////////////////////////////////////////////////////
/// @return		The current reference count on this table.
///
/// Get the current reference count on this table.
//////////////////////////////////////////////////////////////////////////////
uint32
StrTable::GetRefCount()
{
	return m_nRefCount;
}

//////////////////////////////////////////////////////////////////////////////
/// @return		String reference to the table name.
///
/// Get the name of the string table.
//////////////////////////////////////////////////////////////////////////////
const String& 
StrTable::GetName() const
{
	return m_sTableName;
}

//////////////////////////////////////////////////////////////////////////////
/// sName		The new table name.
///
/// Set's the name of this table.
//////////////////////////////////////////////////////////////////////////////
void
StrTable::SetName( const String& sName )
{
	m_sTableName = sName;
}

bool8
StrTable::LoadFromBinary(void* pResData)
{
	bool8 bRetVal = false;

	// Check and make sure we got a pointer to the data
	if ( pResData )
	{
		// Now, we can start pulling info out, one byte at a time
		uint8* pData = (uint8*)pResData;
		
		// Get Version (currently unused, just skip over it)
		// uint16 nVersion = *(uint16*)pData;
		pData += 2;
		
		// Get Count
		uint32 nCount = *(uint32*)pData;
		pData += 4;
		
		// Now, load all the string entries
		for( uint32 i = 0; i < nCount; i++ )
		{
			// Get the size of the short string
			uint32 nDataSize = *(uint32*)pData;
			pData += 4;
			
			// Get the short string
			String sKey( (uint16*)pData, nDataSize );
			pData += nDataSize;
			
			// Get the size of the actual string
			nDataSize = *(uint32*)pData;
			pData += 4;
			
			// Get the actual string
			String* pString = new String( (uint16*)pData, nDataSize );
			pData += nDataSize;
			
			// Add the string to the table
			m_pTable->add( sKey, pString );
		}
		
		// Check for EOF marker
		uint32 nEOF = *(uint32*)pData;
		ASSERT( nEOF == 0xFFFFFFFF );
		
		// We completed the load
		if ( nEOF == 0xFFFFFFFF )
		{
			bRetVal = true;
		}
	}
	
	return bRetVal;
}
	
